/**
 *  Modul mTimer pro operacni system AltairOS
 *
 *  modul se zavesi na preruseni int 1Ch,
 *  kde bude obsluhovat tento interrupt
 *  (provadi se kazdych cca 55ms)
 */

#include <altair/module.h>
#include <stdlib/printf.h>
#include <string/strlen.h>
#include <altair/system/hook.h> /* poke() */
#include "source/mods/mtimer/time.h"

/* na teto adrese se nachazi pocet tiku,
 * s kazdym 65535 tikem probehne reset timeru (na nulu)
 * k vyuziti hlavne kvuli fci sleep() ve stdlib.h
 * (pokud neni tento modul nacten, nebude sleep fungovat)
 *
 * tato hodnota je i definovana v stdlib/stdlib.h (stdlib/sleep.h)
 */
#define CLOCK_TICK (*(unsigned int far *)(SYSADDR + 0x2000))

/**
 *  int_timer_handler
 *  je volan z funkce int_timer
 *
 *  _uptime  = pocet sekund od spusteni
 *  _uptime_last = posledni sekunda, ktera probehla
 */
int  _int_timer_handler_uptime      = 0;
char _int_timer_handler_uptime_last = 0;

void _int_timer_handler(void) {
  static char row[80];
  static struct Time t;
  int i;

  /* zjistime cas */
  gettime(&t);
  if(_int_timer_handler_uptime_last != t.sec) {         /* pokud pricitame sekundu */
    _int_timer_handler_uptime_last = t.sec;
    _int_timer_handler_uptime++;
  }

  /* tisk uptimu */
  sprintf(row, "Uptime: %02d minut a %02d sekund",
    (_int_timer_handler_uptime/60),
      (_int_timer_handler_uptime%60));

  #define PANEL_COL_ATTR (0x70)

  /* pouze vykreslime vrchni panel */
  for(i=0; i<80; i++)
    poke(0xB800,((0 /* pozice znaku */ +i)+(80*(0 /* radek */ )))*2,(PANEL_COL_ATTR<<8)|' ');

  for(i=0; i<strlen(row); i++)
    poke(0xB800,((0 /* pozice znaku */ +i)+(80*(0 /* radek */ )))*2,(PANEL_COL_ATTR<<8)|(row[i]&0xFF));

  /* tisk hodin */
  sprintf(row, "%02d%c%02d",  t.hour,(t.sec%2?' ':':'),t.min);
  for(i=0; i<5; i++)
    poke(0xB800,((75 /* pozice znaku */ +i)+(80*(0 /* radek */ )))*2,(PANEL_COL_ATTR<<8)|(row[i]&0xFF));

  #undef PANEL_COL_ATTR

  /* pricteni tiku */
  CLOCK_TICK++;

  return;
}

/**
 *  interrupt (int 0x1C)
 *  obsluzna rutina
 */
void interrupt _interrupt(void) {
  asm cli                 ;

  /* zazalohujeme si dulezite registry */
  asm push ds             ;
  asm push es             ;
  asm push si             ;
  asm push di             ;

  /* ulozime si SP a SS do nejakych registru */
  asm mov si, sp          ;
  asm mov cx, ss          ;

  /* pocachrujeme s registry a segmenty */
  asm mov dx, cs          ;
  asm mov ds, dx          ;
  asm mov es, dx          ;
  asm mov ss, dx          ;

  /* nyni prisla chvile pushnout si SS a SP a tim si je zalohovat */
  asm push si             ;
  asm push cx             ;

  /* nyni nastavime registr AX a BX na projeti API Handlerem */
  asm sti                 ;

  _int_timer_handler();

  asm cli                 ;

  /* nyni vratime zpet SP a SS */
  asm pop cx              ;
  asm pop si              ;
  asm mov ss, cx          ;
  asm mov sp, si          ;

  /* a DI, SI, ES a DS */
  asm pop di              ;
  asm pop si              ;
  asm pop es              ;
  asm pop ds              ;
  asm sti                 ;

  /* a hura z5 do programu =) */
  return;
}

/* hookujeme int 1Ch */
int _interrupt_no = 0x1C;
int main() {
  _interrupt();
  return 0;
}
